package nl.utwente.ewi.hmi.multitouch;

import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import nl.utwente.ewi.hmi.multitouch.io.TouchStream;
import nl.utwente.ewi.hmi.multitouch.io.TouchStreamProvider;

/**
 * The TouchEnvironment provides a central interface to managing {@link TouchDevice TouchDevices}. By
 * registering a {@linkplain TouchStreamProvider} new {@linkplain TouchStream TouchStream(s)} are exposed
 * to the TouchEnvironment and more {@link TouchDevice TouchDevices} can be managed.
 * 
 * @author Michiel Hakvoort
 * @version 1.0
 *
 */
public class TouchEnvironment {

	private static TouchEnvironment instance = null;

	private List<TouchDeviceInfo> touchDeviceInfos = null;

	private Map<TouchDeviceInfo, TouchStreamProvider> registeredTouchInfos = null;

	private Map<TouchDeviceInfo, TouchDevice> registeredTouchDevices = null;

	private TouchEnvironment() {
		this.registeredTouchDevices = new HashMap<TouchDeviceInfo, TouchDevice>();
		this.touchDeviceInfos = new ArrayList<TouchDeviceInfo>();
		this.registeredTouchInfos = new HashMap<TouchDeviceInfo, TouchStreamProvider>();
	}

	/**
	 * Get the TouchEnvironment instance.
	 * 
	 * @return The TouchEnvironment instance
	 */
	public static TouchEnvironment getTouchEnvironment() {
		if(TouchEnvironment.instance == null) {
			TouchEnvironment.instance = new TouchEnvironment();
		}
		
		return TouchEnvironment.instance;
	}

	/**
	 * Get the list of all {@link TouchDeviceInfo TouchDeviceInfos} known by the TouchEnvironment.
	 *  
	 * @return The list of all {@linkplain TouchDeviceInfo TouchDeviceInfos} known by the TouchEnvironment
	 */
	public List<TouchDeviceInfo> getTouchDeviceInfoList() {
		return Collections.unmodifiableList(this.touchDeviceInfos);
	}

	/**
	 * Create a {@linkplain TouchDevice} for the specified {@linkplain TouchDeviceInfo}. If a TouchDeviceInfo
	 * could not lead back to a TouchDevice, an {@linkplain IllegalArgumentException} is thrown.
	 * 
	 * @param touchDeviceInfo The {@linkplain TouchDeviceInfo} to create a {@linkplain TouchDevice} for
	 * @throws IllegalArgumentException Thrown when the TouchDeviceInfo does not reflect a registered TouchStream
	 * @return The created {@linkplain TouchDevice}
	 */
	public TouchDevice getTouchDevice(TouchDeviceInfo touchDeviceInfo) {

		if(touchDeviceInfo == null) {
			throw new NullPointerException();
		}

		if(!registeredTouchInfos.containsKey(touchDeviceInfo)) {
			String message = "Device \"" + touchDeviceInfo.getDeviceID() + "\" is not registered";
			throw new IllegalArgumentException(message);
		}

		TouchDevice device = registeredTouchDevices.get(touchDeviceInfo);
		
		if(device == null) {
			TouchStreamProvider source = registeredTouchInfos.get(touchDeviceInfo);
			TouchStream stream = source.getTouchStream(touchDeviceInfo);

			device = new TouchDevice(stream);

			registeredTouchDevices.put(touchDeviceInfo, device);
		}

		return device;
	}

	/**
	 * Register a {@linkplain TouchStreamProvider} to the TouchEnvironment. When the {@linkplain TouchStreamProvider} is
	 * added, the TouchEnvironment also offers {@linkplain TouchDeviceInfo TouchDeviceInfos} offered by the 
	 * TouchStreamProvider.
	 * 
	 * @param touchStreamProvider The {@linkplain TouchStreamProvider} to register
	 */
	public void registerTouchStreamProvider(TouchStreamProvider touchStreamProvider) {

		if(touchStreamProvider == null) {
			throw new NullPointerException();
		}

		for(TouchDeviceInfo touchDeviceInfo : touchStreamProvider.getTouchDeviceInfoList()) {
			touchDeviceInfos.add(touchDeviceInfo);
			this.registeredTouchInfos.put(touchDeviceInfo, touchStreamProvider);
		}
	}

	public TouchDeviceInfo getTouchDeviceInfo(URI uri) {
		return null;
	}
}
